package Utils

import (
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
	"strings"
)

var (
	PgmType_P5 = "P5"
)

type PgmFile struct {
	FileName    string
	Type        string // P3 P5
	Info        string
	Width       int
	Height      int
	PixelLength int
	Img         []byte
	SeqWidth    int
	cutX        int
	cutY        int
}

func (pf PgmFile) GetCutXY() (int, int) {
	return pf.cutX, pf.cutY
}

// 因为每一次
func (pf *PgmFile) ResetCutXY() {
	pf.cutX = 0
	pf.cutY = 0
}

func SaveAsPgm(img []byte, width, height int, path string) {
	pgm := PgmFile{
		Type:        "P5",
		Img:         img,
		Info:        "Create by IBAS",
		Width:       width,
		Height:      height,
		PixelLength: 255,
	}
	pgm.Save(path)
}

func (pf PgmFile) Save(path string) {
	if file, err := os.Create(path); err != nil {
		panic(err)
	} else {
		file.Seek(0, io.SeekStart)
		file.Write([]byte(pf.Type))
		file.Write([]byte{10})
		if pf.Info[0] != '#' {
			file.Write([]byte("#"))
			file.Write([]byte{32})
			file.Write([]byte(pf.Info))
		} else {
			file.Write([]byte(pf.Info))
		}
		file.Write([]byte{10})
		file.Write([]byte(strconv.Itoa(pf.Width)))
		file.Write([]byte{32})
		file.Write([]byte(strconv.Itoa(pf.Height)))
		file.Write([]byte{10})
		file.Write([]byte(strconv.Itoa(pf.PixelLength)))
		file.Write([]byte{10})
		file.Write(pf.Img)
		file.Close()
	}
}

func (pf PgmFile) ToString() string {
	return `FileName : ` + pf.FileName +
		"\nType : " + pf.Type +
		"\nInfo : " + pf.Info +
		"\nWidth : " + strconv.Itoa(pf.Width) +
		"\nHeight : " + strconv.Itoa(pf.Height) +
		"\nPixelLength : " + strconv.Itoa(pf.PixelLength)
}

func (pf *PgmFile) OpenFile() {
	file, err := os.OpenFile(pf.FileName, os.O_RDONLY, 0777)
	var fileSize int64
	if err != nil {
		panic(err)
	}
	defer file.Close()
	if stat, err := file.Stat(); err != nil {
		panic(err)
	} else {
		fileSize = stat.Size()
	}
	fmt.Println("file size = " + strconv.FormatInt(fileSize, 10))

	infoBuf := make([]byte, 100)
	var offset = 1
	if _, err := file.Read(infoBuf); err != nil {
		panic(err)
	} else {
		sector := 1
		from := 0
		var ind int
		var b byte
		for ind, b = range infoBuf {
			if b == 10 {
				if sector == 1 {
					pf.Type = string(infoBuf[from:ind])
				} else if sector == 2 {
					pf.Info = string(infoBuf[from:ind])
				} else if sector == 3 {
					s := string(infoBuf[from:ind])
					part := strings.Split(s, " ")
					if len(part) == 2 {
						pf.Width, _ = strconv.Atoi(part[0])
						pf.Height, _ = strconv.Atoi(part[1])
					} else {
						fmt.Println("err")
					}
				} else if sector == 4 {
					pf.PixelLength, _ = strconv.Atoi(string(infoBuf[from:ind]))
					break
				} else {
					break
				}
				sector++
				from = ind + 1
			}
		}
		offset = ind
		fmt.Println("offset = " + strconv.Itoa(ind))
	}
	// read image
	file.Seek(int64(offset+1), io.SeekStart)
	infoBuf = make([]byte, pf.Height*pf.Width)
	if length, err := file.Read(infoBuf); err != nil {
		panic(err)
	} else {
		if length != pf.Height*pf.Width {
			panic("length = " + strconv.Itoa(length) + " not equal [pf.Height * pf.Width] " + strconv.Itoa(pf.Height*pf.Width))
		} else {
			pf.Img = infoBuf
			fmt.Println("image read success")
		}
	}
	pf.SetSeqWidth()
}

func (pf *PgmFile) SetSeqWidth() {
	pf.SeqWidth = pf.Height
	if pf.Width > pf.Height {
		pf.SeqWidth = pf.Width
	}
	pf.SeqWidth = int(math.Floor(float64(pf.SeqWidth)*math.Sqrt(2))) + 1
}

func (pf *PgmFile) checkStartPoint(bytes []byte) (int, int) {
	if pf.cutX != 0 && pf.cutY != 0 {
		return pf.cutX, pf.cutY
	}
	startX := 0
	startY := 0
	maxX := 0
	maxY := 0
	loc := 0
	mergeX := make([]int, pf.SeqWidth)
	mergeY := make([]int, pf.SeqWidth)
	// 统计非零点的个数
	for i := 0; i < pf.SeqWidth; i++ {
		for j := 0; j < pf.SeqWidth; j++ {
			loc = i + j*pf.SeqWidth
			if bytes[loc] > 0 {
				mergeX[i]++
				mergeY[j]++
			}
		}
	}
	// 获取非零点的最大值
	for i := 0; i < pf.SeqWidth; i++ {
		if maxX < mergeX[i] {
			maxX = mergeX[i]
		}
		if maxY < mergeY[i] {
			maxY = mergeY[i]
		}
	}
	xrange := []int{0, 0}
	checkW := pf.Width - 1
	//  132  0~131
	for xrange[1]-xrange[0] < checkW {
		if maxX == 0 {
			break
		}
		i := 0
		for ; i < pf.SeqWidth; i++ {
			if mergeX[i] >= maxX {
				xrange[0] = i
				break
			}
		}
		for ; i < pf.SeqWidth; i++ {
			if mergeX[i] < maxX {
				xrange[1] = i
				break
			}
		}
		maxX--
	}
	if xrange[1]-xrange[0] < checkW {
		startX = 0
	} else {
		startX = xrange[0]
		pf.cutX = startX
	}
	xrange[0] = 0
	xrange[1] = 0
	checkW = pf.Height - 1
	for xrange[1]-xrange[0] < checkW {
		if maxY == 0 {
			break
		}
		i := 0
		for ; i < pf.SeqWidth; i++ {
			if mergeY[i] >= maxY {
				xrange[0] = i
				break
			}
		}
		for ; i < pf.SeqWidth; i++ {
			if mergeY[i] < maxY {
				xrange[1] = i
				break
			}
		}
		maxY--
	}
	if xrange[1]-xrange[0] < checkW {
		startY = 0
	} else {
		startY = xrange[0]
		pf.cutY = startY
	}
	if startX >= 5 {
		fmt.Print("x begin:")
		fmt.Println(mergeX[startX-5 : startX+5])
		fmt.Print("x end:")
		fmt.Println(mergeX[startX+pf.Width-4 : startX+pf.Width+5])
	}
	if startY >= 5 {
		fmt.Print("y begin:")
		fmt.Println(mergeY[startY-5 : startY+5])
		fmt.Print("y end:")
		fmt.Println(mergeY[startY+pf.Height-4 : startY+pf.Height+5])
	}
	return startX, startY
}

// 将图片裁剪为当前的图片大小（因为旋转后图像会变大，这里是用来将旋转两次后，旋转角度为0时的图像的裁剪）
func (pf *PgmFile) CutToSize(bytes []byte) []byte {
	ret := make([]byte, pf.Width*pf.Height)
	halfWidth, halfHeight := pf.checkStartPoint(bytes)
	for w := 0; w < pf.Width; w++ {
		for h := 0; h < pf.Height; h++ {
			loc := (h+halfHeight)*pf.SeqWidth + w + halfWidth
			ret[w+h*pf.Width] = bytes[loc]
		}
	}
	return ret
}

func (pf PgmFile) Cut(bytes []byte) ([]byte, int, int, byte) {
	imgLen := pf.Width * pf.Height
	var max byte = 0
	if len(bytes) == imgLen {
		differentPointCount := 0
		var differentSum int = 0
		result := make([]byte, imgLen)
		for i := 0; i < imgLen; i++ {
			if pf.Img[i] > bytes[i] {
				result[i] = pf.Img[i] - bytes[i]
			} else {
				result[i] = pf.Img[i] - bytes[i]
			}
			differentSum += int(result[i])
			if max < result[i] {
				max = result[i]
			}
			if result[i] != 0 {
				differentPointCount++
			}
		}
		//result := make([]byte, len(bytes))
		//for i := 0;i < len(bytes);i++ {
		//	result[i] = 0
		//}
		//halfWidth,halfHeight := pf.checkStartPoint(bytes)
		////halfWidth++
		////halfHeight++
		//fmt.Println("pf.SeqWidth = " + strconv.Itoa(pf.SeqWidth))
		//fmt.Println("from width = " + strconv.Itoa(halfWidth))
		//fmt.Println("from height = " + strconv.Itoa(halfHeight))
		//for w := 0;w < pf.Width;w++ {
		//	for h := 0;h < pf.Height;h++ {
		//		loc := (h + halfHeight) * pf.SeqWidth + w + halfWidth
		//		if bytes[loc] > pf.Img[w + h * pf.Width] {
		//			result[loc] = bytes[loc] - pf.Img[w + h * pf.Width]
		//		} else {
		//			result[loc] = pf.Img[w + h * pf.Width] - bytes[loc]
		//		}
		//		if result[loc] != 0 {
		//			differentSum += int(result[loc])
		//			differentPointCount++
		//		}
		//		//if bytes[loc] != pf.Img[w + h * pf.Width] {
		//		//	result[loc] = 255
		//		//}
		//	}
		//}
		return result, differentPointCount, differentSum, max
	} else {
		panic("can not to cut, because size is wrong")
	}
}
